package cn.mujiankeji.mbrowser.app

import android.util.Log
import cn.mujiankeji.mbrowser.功能.下载.仓库.下载仓库接口
import cn.mujiankeji.mbrowser.功能.下载.模型.下载任务
import cn.mujiankeji.mbrowser.功能.下载.模型.下载引擎类型
import cn.mujiankeji.mbrowser.功能.下载.模型.下载状态
import cn.mujiankeji.mbrowser.功能.下载.模型.下载类型
import cn.mujiankeji.mbrowser.工具.JSON
import cn.mujiankeji.mbrowser.工具.mlog
import cn.mujiankeji.mbrowser.数据.数据库.DBDownload
import cn.mujiankeji.mbrowser.数据.数据库.DaoDownload
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import java.util.Date

/**
 * 下载仓库的实现类
 * 负责转换数据库实体与领域模型对象，实现仓库接口定义的所有方法
 */
class 下载模块(private val dao: DaoDownload) : 下载仓库接口 {
    
    private val TAG = "下载仓库实现"
    private val gson = Gson()
    
    // 定义类型转换工具，用于Json序列化/反序列化请求头
    private val mapType = object : TypeToken<Map<String, String>>() {}.type
    
    /**
     * 获取所有下载任务，按创建时间排序
     */
    override fun 获取所有下载任务(): Flow<List<下载任务>> {
        return dao.获取所有下载任务Flow()
            .map { 列表 -> 列表.map { 实体 -> 实体转换为模型(实体) } }
            .catch { e ->
                mlog(TAG, "获取所有下载任务时发生错误", e)
                emit(emptyList())
            }
    }

    /**
     * 获取所有活动中的下载任务（等待中、正在下载、已暂停、失败但可重试）
     */
    override fun 获取活动下载任务(): Flow<List<下载任务>> {
        val 活动状态列表 = listOf(
            下载状态.等待中.ordinal,
            下载状态.正在下载.ordinal,
            下载状态.已暂停.ordinal,
            下载状态.失败.ordinal
        )
        return dao.获取特定状态任务Flow(活动状态列表)
            .map { 列表 -> 列表.map { 实体 -> 实体转换为模型(实体) } }
            .catch { e ->
                mlog(TAG, "获取活动下载任务时发生错误", e)
                emit(emptyList())
            }
    }

    /**
     * 获取所有已完成的下载任务
     */
    override fun 获取已完成下载任务(): Flow<List<下载任务>> {
        return dao.获取已完成任务Flow(下载状态.已完成.ordinal)
            .map { 列表 -> 列表.map { 实体 -> 实体转换为模型(实体) } }
            .catch { e ->
                mlog(TAG, "获取已完成下载任务时发生错误", e)
                emit(emptyList())
            }
    }

    /**
     * 根据ID获取单个下载任务
     */
    override fun 获取下载任务(任务Id: String): Flow<下载任务?> {
        return dao.获取下载任务Flow(任务Id)
            .map { 实体 -> 实体?.let { 实体转换为模型(it) } }
            .catch { e ->
                mlog(TAG, "获取下载任务(ID: $任务Id)时发生错误", e)
                emit(null)
            }
    }

    /**
     * 添加一个新的下载任务
     * @return 返回任务ID
     */
    override suspend fun 添加下载任务(任务: 下载任务): String {
        return try {
            val 实体 = 模型转换为实体(任务)
            dao.插入下载任务(实体)
            mlog( "添加下载任务成功: ${任务.id}")
            任务.id
        } catch (e: Exception) {
            mlog(TAG, "添加下载任务失败: ${任务.id}", e)
            throw e
        }
    }

    /**
     * 更新现有下载任务
     */
    override suspend fun 更新下载任务(任务: 下载任务) {
        try {
            val 实体 = 模型转换为实体(任务)
            dao.更新下载任务(实体)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 删除指定ID的下载任务
     */
    override suspend fun 删除下载任务(任务Id: String) {
        try {
            dao.删除下载任务(任务Id)
            mlog( "删除下载任务成功: $任务Id")
        } catch (e: Exception) {
            mlog(TAG, "删除下载任务失败: $任务Id", e)
            throw e
        }
    }

    /**
     * 清空所有下载历史记录
     */
    override suspend fun 清空下载历史() {
        try {
            dao.清空所有下载任务()
            mlog( "清空所有下载历史成功")
        } catch (e: Exception) {
            mlog(TAG, "清空所有下载历史失败", e)
            throw e
        }
    }

    /**
     * 获取下载任务的即时快照
     */
    override suspend fun 获取下载任务快照(任务Id: String): 下载任务? {
        return try {
            dao.获取下载任务快照(任务Id)?.let { 实体转换为模型(it) }
        } catch (e: Exception) {
            mlog(TAG, "获取下载任务快照失败: $任务Id", e)
            null
        }
    }

    /**
     * 获取特定状态的所有下载任务快照
     */
    override suspend fun 获取特定状态任务快照列表(状态码: Int): List<下载任务> {
        return try {
            dao.获取特定状态任务快照列表(状态码).map { 实体转换为模型(it) }
        } catch (e: Exception) {
            mlog(TAG, "获取特定状态(${状态码})任务快照列表失败", e)
            emptyList()
        }
    }
    
    /**
     * 将数据库实体转换为领域模型对象
     */
    private fun 实体转换为模型(实体: DBDownload): 下载任务 {
        // 反序列化请求头JSON
        val 请求头: Map<String, String> = try {
            gson.fromJson(实体.请求头, mapType) ?: emptyMap()
        } catch (e: Exception) {
            mlog(TAG, "请求头JSON解析失败: ${实体.请求头}", e)
            emptyMap()
        }

        return 下载任务(
            id = 实体.uid,
            url = 实体.网址,
            目标文件夹 = 实体.目标路径,
            文件名 = 实体.文件名,
            文件大小 = 实体.文件大小,
            已下载大小 = 实体.已下载大小,
            状态 = 下载状态.values().getOrElse(实体.状态) { 下载状态.等待中 },
            创建时间 = Date(实体.创建时间),
            完成时间 = 实体.完成时间?.let { Date(it) },
            引擎类型 = 下载引擎类型.values().getOrElse(实体.引擎类型) { 下载引擎类型.KTOR },
            请求头 = 请求头,
            UserAgent = 实体.UserAgent,
            类型 = 下载类型.values().getOrElse(实体.类型) { 下载类型.普通 },
            优先级 = 实体.优先级,
            重试次数 = 实体.重试次数,
            错误信息 = 实体.错误信息,
            分块大小 = 实体.分块大小?:0,
            分块数量 = 实体.分块数量?:0,
            分块状态 = 实体.分块状态?.let { try { JSON.decodeFromString(it) }catch (e : Exception){null} }
        )
    }
    
    /**
     * 将领域模型对象转换为数据库实体
     */
    private fun 模型转换为实体(模型: 下载任务): DBDownload {
        // 序列化请求头为JSON
        val 请求头JSON = try {
            gson.toJson(模型.请求头)
        } catch (e: Exception) {
            mlog(TAG, "请求头序列化为JSON失败", e)
            "{}"
        }
        
        return DBDownload(
            uid = 模型.id,
            网址 = 模型.url,
            目标路径 = 模型.目标文件夹,
            文件名 = 模型.文件名,
            文件大小 = 模型.文件大小,
            已下载大小 = 模型.已下载大小,
            状态 = 模型.状态.ordinal,
            创建时间 = 模型.创建时间.time,
            完成时间 = 模型.完成时间?.time,
            引擎类型 = 模型.引擎类型.ordinal,
            请求头 = 请求头JSON,
            UserAgent = 模型.UserAgent,
            类型 = 模型.类型.ordinal,
            优先级 = 模型.优先级,
            重试次数 = 模型.重试次数,
            错误信息 = 模型.错误信息,
            分块大小 =  模型.分块大小,
            分块数量 = 模型.分块数量,
            分块状态 = 模型.分块状态?.let { JSON.encodeToString(it) }
        )
    }
}